﻿using System.Collections.Generic;
using UnityEngine;

[DisallowMultipleComponent]
public class MyLoopGrid : UIGrid
{
    [SerializeField]
    GameObject ItemPrefab;
    /// <summary>
    /// 物件刷新代理事件
    /// </summary>
    ItemCallBack OnItemChange;
    /// <summary>
    /// Item点击代理事件
    /// </summary>
    ItemCallBack OnItemClick;

    UIScrollView mScroll;
    /// </summary>
    /// 当前页要显示的对象上表
    /// <summary>
    public int TopIndex;
    /// <summary>
    ///当前列表Item总数 
    /// </summary>
    private int maxItemNum;
    /// <summary>
    /// 当前列表总行数或者总列数
    /// </summary>
    private int maxRanks;
    /// <summary>
    /// 列表布局有几行或者几列 
    /// </summary>
    private int maxPer;
    /// <summary>
    /// Item的 width 或者 height
    /// </summary>
    private float distance;

    private int minIndex;
    private int maxIndex;
    private int sceneCount;
    private int dataCount;

    protected override void Start() { }

    protected override void Update() { }

    protected override void Init() { base.Init(); }

    protected override void Sort(List<Transform> list) { }

    protected override void ResetPosition(List<Transform> list)
    {
        base.ResetPosition(list);
    }

    public override void Reposition()
    {
        hideInactive = false; animateSmoothly = false;
        List<Transform> list = new List<Transform>();
        for (int i = 0; i < transform.childCount; ++i)
        {
            list.Add(transform.GetChild(i));
        }

        if (sorting != Sorting.None && arrangement != Arrangement.CellSnap)
        {
            if (sorting == Sorting.Alphabetic) list.Sort(SortByName);
            else if (sorting == Sorting.Horizontal) list.Sort(SortHorizontal);
            else if (sorting == Sorting.Vertical) list.Sort(SortVertical);
            else if (onCustomSort != null) list.Sort(onCustomSort);
            else Sort(list);
        }

        ResetPosition(list);    //刷新列表排序位置
        if (keepWithinPanel) ConstrainWithinPanel();
        if (onReposition != null) onReposition();
    }

    void Awake()
    {
        base.Start(); float num; maxPer = maxPerLine;
        mScroll = mPanel.GetComponent<UIScrollView>();

        if (maxPer <= 0) { maxPer = 1; }
        if (mScroll.canMoveHorizontally)
        {
            distance = cellWidth;
            num = mPanel.width / distance;
        }
        else
        {
            distance = cellHeight;
            num = mPanel.height / distance;
        }

        sceneCount = Mathf.FloorToInt(num) * maxPer;
        maxRanks = Mathf.FloorToInt(num) + 1;
        maxItemNum = maxRanks * maxPer;
        if (ItemPrefab != null) { InitData(); }
    }
    
    public void InitData()
    {
        if (ItemPrefab)
        {
            transform.AddChildren(ItemPrefab, maxItemNum);
        }
        for (int i = 0; i < transform.childCount; i++)
        {
            var go = transform.GetChild(i).gameObject;
            UIEventListener.Get(go).onClick = OnClickItem;
        }
        ResetPosition(); Utility.SetActive(ItemPrefab, false);
    }
    
    void OnMove(UIPanel panel)
    {
        if (dataCount <= maxItemNum) { return; }
        Vector3[] corners = panel.worldCorners;
        for (int i = 0; i < 4; ++i)
        {
            corners[i] = transform.InverseTransformPoint(corners[i]);
        }
        Vector3 center = Vector3.Lerp(corners[0], corners[2], 0.5f);

        float ext2 = distance * maxRanks;
        float ext1 = ext2 * 0.5f;
        bool allWithinRange = true;

        for (int i = 0; i < transform.childCount; i++)
        {
            Transform t = transform.GetChild(i);
            int Idx = int.Parse(t.name);
            Vector3 pos = t.localPosition;

            if (mScroll.canMoveHorizontally)
            {
                float dis = pos.x - center.x;
                if (dis < -ext1)
                {
                    Idx += maxItemNum;
                    pos.x += ext2;

                    if (!UpdatePos(t, Idx, pos))
                    {
                        allWithinRange = false;
                    }
                }
                else if (dis > ext1)
                {
                    Idx -= maxItemNum;
                    pos.x -= ext2;

                    if (!UpdatePos(t, Idx, pos))
                    {
                        allWithinRange = false;
                    }
                }
            }
            else
            {
                float dis = pos.y - center.y;
                if (dis < -ext1)
                {
                    Idx -= maxItemNum;
                    pos.y += ext2;

                    if (!UpdatePos(t, Idx, pos))
                    {
                        allWithinRange = false;
                    }
                }
                else if (dis > ext1)
                {
                    Idx += maxItemNum;
                    pos.y -= ext2;

                    if (!UpdatePos(t, Idx, pos))
                    {
                        allWithinRange = false;
                    }
                }
            }
        }
        mScroll.restrictWithinPanel = !allWithinRange;
    }

    public void UpDataList(int count)
    {
        mPanel.onClipMove = null;
        dataCount = count; ResetPosition();
        mPanel.onClipMove = OnMove;
        mScroll.restrictWithinPanel = true;

        float rank = dataCount / maxPer;
        if (dataCount % maxPer == 0)
        {
            rank = rank - 1;
        }
        if (mScroll.canMoveHorizontally)
        {
            maxIndex = +Mathf.CeilToInt(rank);
        }
        else
        {
            minIndex = -Mathf.CeilToInt(rank);
        }

        for (int i = 0; i < transform.childCount; i++)
        {
            Transform t = transform.GetChild(i);
            if (i < dataCount)
            {
                t.name = i.ToString();
                OnChangeItem(t, i);
            }
            Utility.SetActive(t, i < dataCount);
        }
        Utility.SetBehaviour<UIDragScrollView>(transform, sceneCount <= dataCount);
    }

    public void SetTopIndex(int topIdx)
    {
        int endIdx = dataCount - 1 - sceneCount;
        if (TopIndex != topIdx) { TopIndex = topIdx; }
        if (TopIndex > endIdx) { TopIndex = endIdx; }
        if (TopIndex < 0) { TopIndex = 0; }

        int num = Mathf.FloorToInt(TopIndex / maxPer);
        for (int i = 0; i < transform.childCount; i++)
        {
            Transform t = transform.GetChild(i);
            int startIdx = num * maxPer + i;
            t.name = startIdx.ToString();
            Vector2 addPos = GetVector(startIdx);

            float x = addPos.x * cellWidth;
            float y = addPos.y * cellHeight;
            t.localPosition = new Vector3(x, y, 0);

            bool show = startIdx < dataCount;
            Utility.SetActive(t, show);
            if (show) { OnChangeItem(t, startIdx); }
        }

        int count = maxPer * Mathf.FloorToInt(dataCount / maxPer);
        if (dataCount % maxPer == 0) { count = count - maxPer; }
        if (topIdx < count)
        {
            TweenCenter(transform.GetChild(0));
        }
        else
        {
            TweenCenter(transform.GetChild(maxPer));
        }
    }

    void TweenCenter(Transform target)
    {
        mScroll.InvalidateBounds(); Bounds b1 = mScroll.bounds;
        Bounds b2 = NGUIMath.CalculateRelativeWidgetBounds(mScroll.transform, target);

        float hx = mPanel.finalClipRegion.z * 0.5f;
        float hy = mPanel.finalClipRegion.w * 0.5f;
        float left = b1.min.x + hx;
        float right = b1.max.x - hx;
        float bottom = b1.min.y + hy;
        float top = b1.max.y - hy;

        if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
        {
            left -= mPanel.clipSoftness.x;
            right += mPanel.clipSoftness.x;
            bottom -= mPanel.clipSoftness.y;
            top += mPanel.clipSoftness.y;
        }
        if (mScroll.movement == UIScrollView.Movement.Horizontal)
        {
            float width = right - left;
            float x = b2.min.x - b1.min.x + mPanel.clipSoftness.x;
            mScroll.SetDragAmount(width == 0 ? 0 : x / width, 0, false);
        }

        else if (mScroll.movement == UIScrollView.Movement.Vertical)
        {
            float height = top - bottom;
            float y = b1.max.y - b2.max.y + mPanel.clipSoftness.y;
            mScroll.SetDragAmount(0, height == 0 ? 0 : y / height, false);
        }
        mScroll.RestrictWithinBounds(false, mScroll.canMoveHorizontally, mScroll.canMoveVertically);
    }

    void UpdateItem(Transform t, int idx)
    {
        bool bShow = idx >= 0 && idx < dataCount;
        if (bShow)
        {
            OnChangeItem(t, idx);
        }
        Utility.SetActive(t, bShow);
    }

    Vector2 GetVector(int idx)
    {
        Vector2 vector = new Vector2();
        if (mScroll.canMoveHorizontally)
        {
            vector.x = Mathf.FloorToInt(idx / maxPer);
            vector.y = -idx % maxPer;
        }
        else
        {
            vector.x = idx % maxPer;
            vector.y = -Mathf.FloorToInt(idx / maxPer);
        }
        return vector;
    }

    bool UpdatePos(Transform t, int Idx, Vector3 pos)
    {
        int realIndex = 0;
        if (mScroll.canMoveHorizontally)
        {
            realIndex = Mathf.RoundToInt(pos.x / cellWidth);
        }
        else
        {
            realIndex = Mathf.RoundToInt(pos.y / cellHeight);
        }

        if (minIndex <= realIndex && realIndex <= maxIndex)
        {
            t.localPosition = pos;
            t.name = Idx.ToString();
            UpdateItem(t, Idx);
            return true;
        }
        return false;
    }

    void OnChangeItem(Transform t, int idx)
    {
        if (OnItemChange != null)
        {
            OnItemChange(t.gameObject, idx);
        }
    }

    void OnClickItem(GameObject go)
    {
        int idx = int.Parse(go.name);
        if (OnItemClick != null)
        {
            OnItemClick(go, idx);
        }
    }

    public void SetDelegate(ItemCallBack onItemChange, ItemCallBack onItemClick)
    {
        OnItemChange = onItemChange;
        OnItemClick = onItemClick;
    }

    public void ResetPosition()
    {
        Reposition();
        mScroll.ResetPosition();
    }
}